package com.example.demo38exceptionhandling.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

/**
 * ExceptionTranslationFilter 是处理认证和权限异常的过滤器
 *
 * 1. 过滤器最核心的当然是 doFilter 方法，我们就从 doFilter 方法看起。这里的 doFilter 方法中过滤器链继续向下执行，ExceptionTranslationFilter 处于 Spring Security 过滤器链的倒数第二个，最后一个是 FilterSecurityInterceptor，FilterSecurityInterceptor 专门处理授权问题，在处理授权问题时，就会发现用户未登录、未授权等，进而抛出异常，抛出的异常，最终会被 ExceptionTranslationFilter#doFilter 方法捕获。
 * 2. 当捕获到异常之后，接下来通过调用 throwableAnalyzer.getFirstThrowableOfType 方法来判断是认证异常还是授权异常，判断出异常类型之后，进入到 handleSpringSecurityException 方法进行处理；如果不是 Spring Security 中的异常类型，则走 ServletException 异常类型的处理逻辑。
 * 3. 进入到 handleSpringSecurityException 方法之后，还是根据异常类型判断，如果是认证相关的异常，就走 sendStartAuthentication 方法，最终被 authenticationEntryPoint.commence 方法处理；如果是授权相关的异常，就走 accessDeniedHandler.handle 方法进行处理。
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    MyAuthenticationEntryPoint myAuthenticationEntryPoint;
    @Autowired
    MyAccessDeniedHandler myAccessDeniedHandler;

    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    @Bean
    protected UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        String encodePwd = passwordEncoder().encode("123");
        inMemoryUserDetailsManager.createUser(User.withUsername("wangnian").password(encodePwd).roles("admin").build());
        inMemoryUserDetailsManager.createUser(User.withUsername("zhangsan").password(encodePwd).roles("user").build());
        return inMemoryUserDetailsManager;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                .antMatchers("/user/**").hasAnyRole("admin","user")
                .anyRequest().authenticated()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(myAuthenticationEntryPoint)
                .accessDeniedHandler(myAccessDeniedHandler)
                .and().formLogin()
                .permitAll()
                .and().csrf().disable();
    }
}
